package com.piece.core.web.interceptor;

import com.piece.core.framework.annotation.source.CacheSource;
import com.piece.core.framework.constant.FrameWorkConstants;
import com.piece.core.framework.util.cache.Cache;
import com.piece.core.framework.util.lock.DistributedLock;
import com.piece.core.framework.util.lock.ZLock;
import com.piece.core.framework.util.math.RandomUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.string.StringUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 所有service层数据缓存切面
 */
public class CacheServiceInterceptor implements MethodInterceptor {

    private final DistributedLock locker;
    private final Cache cache;

    public CacheServiceInterceptor(DistributedLock locker, Cache cache) {
        this.locker = locker;
        this.cache = cache;
    }

    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        // 获取目标方法的参数列表
        Object[] args = invocation.getArguments();
        // 获取目标方法的返回值类型
        Class<?> returnType = method.getReturnType();

        CacheSource cacheSource = method.getAnnotation(CacheSource.class);
        if (null == cacheSource || returnType.equals(void.class)) {
            return invocation.proceed();
        }

        // 从缓存中查询
        if (null != locker) {
            String key = invocation.getClass().getSimpleName() + FrameWorkConstants.SEPARATOR_KEY_VALUE +
                    method.getName();
            String json = cache.get(key);
            // 命中返回
            if (StringUtil.isNotEmpty(json)) {
                return JsonUtil.toBean(json, returnType);
            }

            ZLock lockObj = null;
            try {
                // 没有命中,加分布式锁
                lockObj = locker.lock(key);
                // 再次查询缓存,命中返回
                json = cache.get(key);
                // 命中返回
                if (StringUtil.isNotEmpty(json)) {
                    locker.unlock(lockObj);
                    return JsonUtil.toBean(json, returnType);
                }

                // 执行目标方法
                Object result = invocation.proceed();
                // 放入缓存,释放分布式锁
                int timeout = cacheSource.timeout();
                int random = cacheSource.random();
                if (timeout != 0) {
                    cache.setExpire(key, JsonUtil.toJson(result), timeout + RandomUtil.randomInt(random), TimeUnit.SECONDS);
                } else {
                    cache.set(key, JsonUtil.toJson(result));
                }
                return result;
            } finally {
                locker.unlock(lockObj);
            }
        }

        return invocation.proceed();
    }
}
